﻿// Copyright © Microsoft Corporation.  All Rights Reserved.
// This code released under the terms of the 
// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)

using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;

namespace TheOliver.Controls
{
    public class Mouse3DBehavior : Behavior<UIElement>
    {
        PlaneProjection _planeProjection;
        bool _buttonPressed = false;
        Point _originPoint;
        UIElement _target;
        FrameworkElement _control;
        UIElement _targetParent;
        double _controlHeight;
        double _controlWidth;

        protected override void OnAttached()
        {
            base.OnAttached();

            _target = this.AssociatedObject;
            _control = _target as FrameworkElement;

            _control.LayoutUpdated += (s, e) =>
            {
                _targetParent = _control.Parent as UIElement;

                _planeProjection = new PlaneProjection();
                _target.Projection = _planeProjection;

                _targetParent.MouseLeftButtonDown += new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown);
                _targetParent.MouseLeftButtonUp += new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonUp);
                _targetParent.MouseMove += new MouseEventHandler(AssociatedObject_MouseMove);
                _targetParent.MouseLeave += new MouseEventHandler(AssociatedObject_MouseLeave);

                _controlHeight = (_targetParent as FrameworkElement).ActualHeight;
                _controlWidth = (_targetParent as FrameworkElement).ActualWidth;
            };
        }

        void AssociatedObject_MouseLeave(object sender, MouseEventArgs e)
        {
            _buttonPressed = false;
        }

        void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            _target.CaptureMouse();
            _originPoint = GetPoint(e);
            _buttonPressed = true;
        }

        void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            _buttonPressed = false;
            _target.ReleaseMouseCapture();
        }

        void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
        {
            if (_buttonPressed)
            {
                PlaneProjection pp = _target.Projection as PlaneProjection;
                Point pt = GetPoint(e);

                double widthPixPerAngle = _controlWidth / 720;
                double heightPixPerAngle = _controlHeight / 720;

                double diffY = (pt.X - _controlWidth / 2) * widthPixPerAngle;
                double diffX = (pt.Y - _controlHeight / 2) * heightPixPerAngle;

                _originPoint = pt;

                if (YEnable)
                {
                    pp.RotationY = diffY;
                }

                if (XEnable)
                {
                    pp.RotationX = diffX;
                }
            }
        }

        private Point GetPoint(MouseEventArgs e)
        {
            Point point = e.GetPosition(_targetParent);
            return point;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            this.AssociatedObject.Projection = null;

            _targetParent.MouseLeftButtonDown -= new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown);
            _targetParent.MouseLeftButtonUp -= new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonUp);
            _targetParent.MouseMove -= new MouseEventHandler(AssociatedObject_MouseMove);
            _targetParent.MouseLeave -= new MouseEventHandler(AssociatedObject_MouseLeave);
        }

        public double MoveFactor
        {
            get { return (double)GetValue(MoveFactorProperty); }
            set { SetValue(MoveFactorProperty, value); }
        }

        public static readonly DependencyProperty MoveFactorProperty =
            DependencyProperty.Register(
                "MoveFactor",
                typeof(double),
                typeof(Mouse3DBehavior),
                new PropertyMetadata(1.0));

        public bool XEnable
        {
            get { return (bool)GetValue(XEnableProperty); }
            set { SetValue(XEnableProperty, value); }
        }

        public static readonly DependencyProperty XEnableProperty =
            DependencyProperty.Register(
                "XEnable",
                typeof(bool),
                typeof(Mouse3DBehavior),
                new PropertyMetadata(true));

        public bool YEnable
        {
            get { return (bool)GetValue(YEnableProperty); }
            set { SetValue(YEnableProperty, value); }
        }

        public static readonly DependencyProperty YEnableProperty =
            DependencyProperty.Register(
                "YEnable",
                typeof(bool),
                typeof(Mouse3DBehavior),
                new PropertyMetadata(true));
    }
}
